﻿using System;
using System.Threading;
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Runtime.InteropServices;
#if SERVICE
using System.IO;
using System.Net.Sockets;
using System.Collections.Concurrent;
#endif
using Cysharp.Threading.Tasks;
using Udx;
using AOT;
using Net.System;
using Net.Event;
using Net.Share;
using Net.Helper;

namespace Net.Client
{
    /// <summary>
    /// udx客户端类型 -> 只能300人以下连接, 如果想要300个客户端以上, 请进入udx网址:www.goodudx.com 联系作者下载专业版FastUdxApi.dll, 然后更换下框架内的FastUdxApi.dll即可
    /// 第三版本 2020.9.14
    /// </summary>
    public class UdxClient : ClientBase
    {
        protected IntPtr udxObj;
        protected IntPtr ClientPtr;
        protected UDXPRC udxPrc;

        ~UdxClient()
        {
#if !UNITY_EDITOR
            Close();
#endif
        }

        public override UniTask<bool> Connect(string host, int port, int localPort, Action<bool> result)
        {
            if (!UdxLib.INIT)
            {
                UdxLib.INIT = true;
#if SERVICE && WINDOWS
                var path = AppDomain.CurrentDomain.BaseDirectory;
                if (!File.Exists(PathHelper.Combine(path, "udxapi.dll")))
                    throw new FileNotFoundException($"udxapi.dll没有在程序根目录中! 请从GameDesigner文件夹下找到 udxapi.dll复制到{path}目录下.");
#endif
                UdxLib.UInit(1);
                UdxLib.UEnableLog(false);
            }
            return base.Connect(host, port, localPort, result);
        }

        protected override void OnCreateSocket(string ipAddress, int port)
        {
            ReleaseUdx();
            udxObj = UdxLib.UCreateFUObj();
            UdxLib.UBind(udxObj, null, 0);
            udxPrc = new UDXPRC(ProcessReceive);
            UdxLib.USetFUCB(udxObj, udxPrc);
            GC.KeepAlive(udxPrc);
        }

        protected override void OnSocketConnect(string ipAddress, int port)
        {
            if (ipAddress == "127.0.0.1" || ipAddress == "localhost")
                ipAddress = NetPort.GetIP();
            ClientPtr = UdxLib.UConnect(udxObj, ipAddress, port, 0, false, 0);
            if (ClientPtr != IntPtr.Zero)
            {
                UdxLib.UDump(ClientPtr);
                var handle = GCHandle.Alloc(this);
                var user = GCHandle.ToIntPtr(handle);
                UdxLib.USetUserData(ClientPtr, user.ToInt64());
            }
        }

        protected override void SendHandshake()
        {
            var segment = BufferPool.Take(Config.SendBufferSize);
            segment.Write(PreUserId);
            segment.Flush();
            SendByteData(segment);
            BufferPool.Push(segment);
        }

        public override void ReceiveHandler()
        {
        }

        //IL2CPP使用Marshal.GetFunctionPointerForDelegate需要设置委托方法为静态方法，并且要添加上特性MonoPInvokeCallback
        [MonoPInvokeCallback(typeof(UDXPRC))]
        protected static void ProcessReceive(UDXEVENT_TYPE type, int erro, IntPtr cli, IntPtr pData, int len)//cb回调
        {
            try
            {
                var user = UdxLib.UGetUserData(cli);
                var handle = GCHandle.FromIntPtr(user);
                var client = handle.Target as UdxClient;
                switch (type)
                {
                    case UDXEVENT_TYPE.E_CONNECT:
                        if (erro != 0)
                            return;
                        client.ClientPtr = cli;
                        client.Connected = true;
                        UdxLib.USetGameMode(cli, true);
                        break;
                    case UDXEVENT_TYPE.E_LINKBROKEN:
                        client.Connected = false;
                        client.NetworkState = NetworkState.ConnectLost;
                        client.MainThreadEnqueue(client.OnConnectLost);
                        client.RpcQueue = new();
                        client.ReleaseUdx();
                        handle.Free();
                        NDebug.Log("断开连接！");
                        break;
                    case UDXEVENT_TYPE.E_DATAREAD:
                        var buffer = BufferPool.Take(len);
                        buffer.Count = len;
                        Marshal.Copy(pData, buffer.Buffer, 0, len);
                        client.ReceiveHandler(ref buffer);
                        BufferPool.Push(buffer);
                        break;
                }
            }
            catch (Exception ex)
            {
                NDebug.LogError("处理异常:" + ex);
            }
        }

        protected unsafe override void SendByteData(ISegment buffer)
        {
            if (ClientPtr == IntPtr.Zero)
                return;
            trafficStatistics.SentPacketsPerSecond++;
            trafficStatistics.SentBytesPerSecond += buffer.Count;
            fixed (byte* ptr = buffer.Buffer)
            {
                int count = UdxLib.USend(ClientPtr, ptr, buffer.Count);
                if (count <= 0)
                    OnSendErrorHandle?.Invoke(buffer);
            }
        }

        public override void Close(bool await = true, int millisecondsTimeout = 100)
        {
            base.Close(await, millisecondsTimeout);
            ReleaseUdx();
        }

        protected void ReleaseUdx()
        {
            if (ClientPtr != IntPtr.Zero)
            {
                UdxLib.UClose(ClientPtr);
                UdxLib.UUndump(ClientPtr);
                ClientPtr = IntPtr.Zero;
            }
            if (udxObj != IntPtr.Zero)
            {
                UdxLib.UDestroyFUObj(udxObj);
                udxObj = IntPtr.Zero;
            }
        }
    }
}